package com.yonyou.brigade.third_parties.didi.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yonyou.brigade.basic_init.enums.ChannelEnum;
import com.yonyou.brigade.basic_init.model.BriChannel;
import com.yonyou.brigade.basic_init.service.IBriChannelService;
import com.yonyou.brigade.third_parties.didi.model.DiDiConfig;
import com.yonyou.brigade.third_parties.didi.model.DidiInvoiceHistory;
import com.yonyou.brigade.third_parties.didi.model.DidiInvoiceOrderHistory;
import com.yonyou.brigade.third_parties.didi.model.third.*;
import com.yonyou.brigade.third_parties.didi.service.IDiDiInvoiceService;
import com.yonyou.brigade.third_parties.didi.service.IDidiInvoiceHistoryService;
import com.yonyou.brigade.third_parties.didi.service.IDidiInvoiceOrderHistoryService;
import com.yonyou.brigade.third_parties.didi.util.DiDiConstants;
import com.yonyou.brigade.third_parties.didi.util.DiDiRMIUtils;
import com.yonyou.brigade.third_parties.didi.util.DiDiResultUtil;
import com.yonyou.brigade.util.oss.AliyunOssUtil;
import com.yonyou.ybzboot.starter.common.exception.BusinessException;
import com.yonyou.ybzboot.starter.common.exception.CustomException;
import com.yonyou.ybzboot.starter.common.util.IDUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * 滴滴发票操作 服务实现类
 *
 * @author kangfw5
 */
@Slf4j
@Service
public class DiDiInvoiceServiceImpl implements IDiDiInvoiceService {
    @Autowired
    private DiDiRMIUtils diDiRMIUtils;
    @Autowired
    private IBriChannelService briChannelService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private AliyunOssUtil aliyunOssUtil;
    @Autowired
    private IDidiInvoiceHistoryService didiInvoiceHistoryService;
    @Autowired
    private IDidiInvoiceOrderHistoryService didiInvoiceOrderHistoryService;

    @Value("${didi.host}")
    private String didiHost;

    @Override
    public List<InvoiceTitle> getTitleList(String tenantId, String orgPk, InvoiceType invoiceType) {
        List<BriChannel> briChannels = briChannelService.getEnableChannelByTenantAndChannel(tenantId, ChannelEnum.DIDI.getName());
        BriChannel briChannel;
        if (CollectionUtils.isEmpty(briChannels)) {
            throw new BusinessException("获取该租户滴滴企业版开通信息失败");
        } else {
            briChannel = briChannels.get(0);
        }
        DiDiConfig diDiConfig = JSON.parseObject(briChannel.getConfig(), DiDiConfig.class);
        String titleResult;
        switch (invoiceType) {
            case GENERAL:
                titleResult = diDiRMIUtils.executeRMIRequest(DiDiConstants.METHOD_GET_INVOICE_TITLE_QUERY, generateGetInvoiceTitleParamsMap(diDiConfig), RequestMethod.GET.name());
                break;
            case SPECIAL:
                titleResult = diDiRMIUtils.executeRMIRequest(DiDiConstants.METHOD_GET_INVOICE_QUALIFICATION, generateGetInvoiceTitleParamsMap(diDiConfig), RequestMethod.GET.name());
                break;
            default:
                throw new BusinessException("尚不支持该发票类型");
        }
        log.info("查询滴滴普票抬头列表，租户{}，参数{}，结果{}", tenantId, invoiceType, titleResult);
        return (List<InvoiceTitle>) DiDiResultUtil.handleResult(titleResult, InvoiceTitle.class);
    }

    @Override
    public String applyForInvoice(String tenantId, String orgPk, String userId, InvoiceApplyParam invoiceApplyParam) {
        DiDiConfig didiConfig = (DiDiConfig) briChannelService.getChannelConfig(tenantId, ChannelEnum.DIDI.getName(), new DiDiConfig());
        if (didiConfig == null) {
            throw new BusinessException("获取该租户滴滴企业版开通信息失败");
        }
        //滴滴开票请求数据处理
        String invoiceNo = IDUtil.get32UUID();
        List<DiDiOrderDTO> orderList = invoiceApplyParam.getOrderList();
        InvoiceApplyData invoiceApplyData = new InvoiceApplyData();
        BigDecimal totalPrice = BigDecimal.ZERO;
        //组装请求订单数据及开票行程记录
        List<InvoiceApplyData.Order> simpleOrderList = new ArrayList<>();
        for (DiDiOrderDTO order : orderList) {
            //开票请求订单数据
            BigDecimal personalRealPay = order.getPersonalRealPay();
            InvoiceApplyData.Order simpleOrder = new InvoiceApplyData.Order();
            simpleOrder.setOrderId(order.getOrderId());
            simpleOrder.setPrice(personalRealPay.toString());
            simpleOrderList.add(simpleOrder);
            totalPrice = totalPrice.add(personalRealPay);
        }
        invoiceApplyParam.setInvoiceNo(invoiceNo);
        invoiceApplyData.setOrders(simpleOrderList);
        invoiceApplyData.setTotalPrice(totalPrice.toString());
        invoiceApplyParam.setData(invoiceApplyData);

        String resultStr = diDiRMIUtils.executeRMIRequest(DiDiConstants.METHOD_POST_INVOICE_APPLY, generatePostInvoiceApplyParams(invoiceApplyParam, didiConfig), RequestMethod.POST.name());
        log.info("滴滴申请开票，租户{}，参数{}，结果{}", tenantId, invoiceApplyParam, resultStr);
        //保存开票历史记录
        DidiInvoiceHistory didiInvoiceHistory = new DidiInvoiceHistory();
        didiInvoiceHistory.setApplyTime(new Date());
        didiInvoiceHistory.setInvoiceNo(invoiceNo);
        //发票状态先不管
        didiInvoiceHistory.setInvoiceStatus(null);
        didiInvoiceHistory.setInvoiceType(3);
        didiInvoiceHistory.setTotalPrice(totalPrice);
        didiInvoiceHistory.setReceiverEmail(invoiceApplyParam.getReceiverEmail());
        didiInvoiceHistory.setInvoiceTitle(invoiceApplyParam.getInvoiceTitle());
        didiInvoiceHistory.setTaxRegistrationNumber(invoiceApplyParam.getTaxRegistrationNumber());
        didiInvoiceHistory.setInvoiceContent(invoiceApplyParam.getInvoiceContent());
        didiInvoiceHistory.setTenant(tenantId);
        didiInvoiceHistory.setOrg(orgPk);
        didiInvoiceHistory.setUserId(userId);
        try {
            JSONObject data = (JSONObject) DiDiResultUtil.handleResult(resultStr, JSONObject.class);
            String invoiceId = data.getString(DiDiConstants.KEY_INVOICE_ID);
            didiInvoiceHistory.setInvoiceId(invoiceId);
            didiInvoiceHistoryService.insert(didiInvoiceHistory);
            //开票行程记录
            List<DidiInvoiceOrderHistory> invoiceOrderHistoryList = new ArrayList<>();
            orderList.forEach(order -> {
                DidiInvoiceOrderHistory invoiceOrder = new DidiInvoiceOrderHistory();
                BeanUtils.copyProperties(order, invoiceOrder);
                invoiceOrder.setInvoiceId(invoiceId);
                invoiceOrder.setTenant(tenantId);
                invoiceOrder.setOrg(orgPk);
                invoiceOrder.setUserId(userId);
                invoiceOrderHistoryList.add(invoiceOrder);
            });
            didiInvoiceOrderHistoryService.insertBatch(invoiceOrderHistoryList);
            return invoiceId;
        } catch (Exception e) {
            didiInvoiceHistory.setSuccess(0);
            didiInvoiceHistoryService.insert(didiInvoiceHistory);
            throw new BusinessException("申请开票失败" + e.getMessage());
        }
    }

    @Override
    public JSONObject queryInvoice(String tenantId, String orgPk, InvoiceQueryParam invoiceQueryParam) {
        DiDiConfig didiConfig = (DiDiConfig) briChannelService.getChannelConfig(tenantId, ChannelEnum.DIDI.getName(), new DiDiConfig());
        if (didiConfig == null) {
            throw new BusinessException("获取该租户滴滴企业版开通信息失败");
        }
        String resultStr = diDiRMIUtils.executeRMIRequest(DiDiConstants.METHOD_GET_INVOICE_GET, generateGetInvoiceGetParams(invoiceQueryParam, didiConfig), RequestMethod.GET.name());
        log.info("查询滴滴发票列表，租户{}，参数{}，结果{}", tenantId, invoiceQueryParam, resultStr);
        return (JSONObject) DiDiResultUtil.handleResult(resultStr, JSONObject.class);
    }

    @Override
    public List<String> downloadInvoice(String tenantId, String invoiceId) {
        DiDiConfig didiConfig = (DiDiConfig) briChannelService.getChannelConfig(tenantId, ChannelEnum.DIDI.getName(), new DiDiConfig());
        if (didiConfig == null) {
            throw new BusinessException("获取该租户滴滴企业版开通信息失败");
        }
        Map<String, Object> params = generateGetInvoiceDownloadParams(invoiceId, didiConfig);
        /*String url = DiDiConstants.DIDI_HOST + DiDiConstants.METHOD_GET_INVOICE_DOWNLOAD
            + "?client_id={client_id}&access_token={access_token}&timestamp={timestamp}"
            + "&company_id={company_id}&invoice_id={invoice_id}&sign={sign}";*/
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(DiDiConstants.DIDI_HOST + DiDiConstants.METHOD_GET_INVOICE_DOWNLOAD);
        //Map请求参数转queryString
        for (String key : params.keySet()) {
            builder.queryParam(key, params.get(key));
        }
        HttpHeaders headers = new HttpHeaders();
        List<MediaType> mediaTypes = new ArrayList<>();
        mediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        mediaTypes.add(MediaType.APPLICATION_JSON);
        headers.setAccept(mediaTypes);
        HttpEntity<String> entity = new HttpEntity<>(headers);
        ResponseEntity<byte[]> exchange = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, entity, byte[].class, params);
        //请求结果处理
        List<String> fileUrlList = new ArrayList<>();
        //返回类型是JSON说明下载发票失败，解析错误信息返回
        if (HttpStatus.OK.equals(exchange.getStatusCode()) && exchange.hasBody() && MediaType.APPLICATION_JSON.equals(exchange.getHeaders().getContentType())) {
            //try-catch-resources
            try (InputStream is = new ByteArrayInputStream(Objects.requireNonNull(exchange.getBody()));
                 BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
                StringBuilder sb = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
                log.info("滴滴电子发票下载返回信息：{}", sb.toString());
                DiDiResultUtil.handleResult(sb.toString(), String.class);
            } catch (IOException e) {
                log.error("滴滴电子发票下载：异常信息解析异常");
            }
        } else if (HttpStatus.OK.equals(exchange.getStatusCode()) && exchange.hasBody() && MediaType.APPLICATION_OCTET_STREAM.equals(exchange.getHeaders().getContentType())) {
            //文件流上传阿里云OSS
            try (ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(Objects.requireNonNull(exchange.getBody())))) {
                ZipEntry ze;
                while ((ze = zin.getNextEntry()) != null) {
                    String fileName = ze.getName();
                    if (!ze.isDirectory() && fileName.endsWith(".pdf")) {
                        ByteArrayOutputStream fileOutputStream = new ByteArrayOutputStream();
                        byte[] bytes = new byte[2048];
                        int num;
                        while ((num = zin.read(bytes, 0, bytes.length)) > -1) {
                            fileOutputStream.write(bytes, 0, num);
                        }
                        InputStream fileInputStream = new ByteArrayInputStream(fileOutputStream.toByteArray());
                        String key = new SimpleDateFormat("yyyyMMddHHmmsssss").format(new Date()) + new Random().nextInt(100000000) + fileName;
                        String filePath = aliyunOssUtil.upload(key, fileInputStream);
                        log.info("滴滴电子发票上传阿里云访问地址" + filePath);
                        fileUrlList.add(filePath);

                        fileOutputStream.close();
                        fileInputStream.close();
                    }
                }
            } catch (IOException e) {
                throw new CustomException("滴滴电子发票解析异常：文件流解析异常");
            }
        } else {
            throw new CustomException("滴滴服务通讯异常：状态码" + exchange.getStatusCodeValue());
        }
        return fileUrlList;
    }

    private Map<String, Object> generateGetInvoiceDownloadParams(String invoiceId, DiDiConfig didiConfig) {
        Map<String, Object> paramsMap = new HashMap<>(8);
        paramsMap.put(DiDiConstants.KEY_COMPANY_ID, didiConfig.getCompanyId());
        paramsMap.put(DiDiConstants.KEY_ACCESS_TOKEN, diDiRMIUtils.getToken(didiConfig));
        paramsMap.put(DiDiConstants.KEY_CLIENT_ID, didiConfig.getClientId());
        paramsMap.put(DiDiConstants.KEY_TIMESTAMP, System.currentTimeMillis());
        paramsMap.put(DiDiConstants.KEY_INVOICE_ID, invoiceId);

        Map<String, Object> applyParamsMap = diDiRMIUtils.keySort(paramsMap, didiConfig);
        String sign = diDiRMIUtils.concatRequestParamsAndMd5Encode(applyParamsMap);
        applyParamsMap.put(DiDiConstants.KEY_SIGN, sign);
        return applyParamsMap;
    }

    /**
     * 组装查询发票信息参数
     *
     * @param didiConfig 滴滴企业版配置信息
     * @return {@link Map} 请求参数
     */
    private Map<String, Object> generateGetInvoiceGetParams(InvoiceQueryParam invoiceQueryParam, DiDiConfig didiConfig) {
        JSONObject paramJson = JSON.parseObject(JSON.toJSONString(invoiceQueryParam));
        paramJson.put(DiDiConstants.KEY_COMPANY_ID, didiConfig.getCompanyId());
        paramJson.put(DiDiConstants.KEY_ACCESS_TOKEN, diDiRMIUtils.getToken(didiConfig));
        paramJson.put(DiDiConstants.KEY_CLIENT_ID, didiConfig.getClientId());
        paramJson.put(DiDiConstants.KEY_TIMESTAMP, System.currentTimeMillis());
        Map<String, Object> applyParamsMap = diDiRMIUtils.keySort(paramJson, didiConfig);
        String sign = diDiRMIUtils.concatRequestParamsAndMd5Encode(applyParamsMap);
        applyParamsMap.put(DiDiConstants.KEY_SIGN, sign);
        return applyParamsMap;
    }

    /**
     * 组装开票申请参数
     *
     * @param didiConfig 滴滴企业版配置信息
     * @return {@link Map} 请求参数
     */
    private Map<String, Object> generatePostInvoiceApplyParams(InvoiceApplyParam invoiceApplyParam, DiDiConfig didiConfig) {
        JSONObject paramJson = JSON.parseObject(JSON.toJSONString(invoiceApplyParam));
        //发票信息为String所以重新放入
        paramJson.put(DiDiConstants.DATA, JSON.toJSONString(invoiceApplyParam.getData()));
        paramJson.put(DiDiConstants.KEY_COMPANY_ID, didiConfig.getCompanyId());
        paramJson.put(DiDiConstants.KEY_CLIENT_ID, didiConfig.getClientId());
        paramJson.put(DiDiConstants.KEY_ACCESS_TOKEN, diDiRMIUtils.getToken(didiConfig));
        paramJson.put(DiDiConstants.KEY_TIMESTAMP, System.currentTimeMillis());
        paramJson.put(DiDiConstants.KEY_INVOICE_CONTENT, DiDiConstants.KEY_INVOICE_CONTENT_VALUE);
        paramJson.remove("orderList");
        Map<String, Object> applyParamsMap = diDiRMIUtils.keySort(paramJson, didiConfig);
        String sign = diDiRMIUtils.concatRequestParamsAndMd5Encode(applyParamsMap);
        applyParamsMap.put(DiDiConstants.KEY_SIGN, sign);
        return applyParamsMap;
    }

    /**
     * 组装查询发票抬头参数
     *
     * @param diDiConfig 滴滴企业版配置信息
     * @return {@link Map} 请求参数
     */
    private Map<String, Object> generateGetInvoiceTitleParamsMap(DiDiConfig diDiConfig) {
        Map<String, Object> paramsMap = new HashMap<>(8);
        paramsMap.put(DiDiConstants.KEY_CLIENT_ID, diDiConfig.getClientId());
        paramsMap.put(DiDiConstants.KEY_TIMESTAMP, System.currentTimeMillis());
        paramsMap.put(DiDiConstants.KEY_ACCESS_TOKEN, diDiRMIUtils.getToken(diDiConfig));
        paramsMap.put(DiDiConstants.KEY_COMPANY_ID, diDiConfig.getCompanyId());
        Map<String, Object> orderParamsMap = diDiRMIUtils.keySort(paramsMap, diDiConfig);
        String sign = diDiRMIUtils.concatRequestParamsAndMd5Encode(orderParamsMap);
        orderParamsMap.put(DiDiConstants.KEY_SIGN, sign);
        return orderParamsMap;
    }
}
